#include "PointerTest.h"
#include "CBL_Defines.h"

#include "CBL_SmartPtr.h"
#include "CBL_ResourcePtr.h"
#include "CBL_ReferencePtr.h"

static uint32 s_DestructorCount;

class TestClass : public Cb::IResource
{
public:
    
    TestClass() : m_Value(0) {}
    ~TestClass() { ++s_DestructorCount; }
    
public:
    
    uint32 m_Value;
};

BEGIN_TEST_CASE_IMPLEMENTATION(CPointerTest)

s_DestructorCount= 0;
uint32 DestructorCalls = 0;

TestClass* pNull = NULL;

//
// Reference Pointer Test
//

TestClass* pRefTest = new TestClass();

Cb::CReferencePtr<TestClass> RefTestPtr1(pRefTest);
Cb::CReferencePtr<TestClass> RefTestPtr2(RefTestPtr1);
Cb::CReferencePtr<TestClass>* pRefTestPtr = new Cb::CReferencePtr<TestClass>(RefTestPtr1);

CHECK(pRefTest == NULL, 					"> CReferencePtr ownership on consructor test failed.")
CHECK(RefTestPtr1.GetReferenceCount() == 3, "> CReferencePtr reference increment failed.");

RefTestPtr1->m_Value = 1;

CHECK(RefTestPtr2->m_Value == 1, 			"> CReferencePtr copy constructor failed.");

RefTestPtr2 = pNull;

CHECK(RefTestPtr1.GetReferenceCount() == 2, "> CReferencePtr reference decrement after NULL asignment failed.");
CHECK(RefTestPtr2.GetReferenceCount() == 0, "> CReferencePtr reference clear after NULL asignment failed.");

delete pRefTestPtr;

CHECK(RefTestPtr1.GetReferenceCount() == 1, "> CReferencePtr reference decrement after destruction failed.");
CHECK(RefTestPtr1.IsLastReference() == 1, 	"> CReferencePtr IsLastReference failed.");

RefTestPtr1.Release();
++DestructorCalls;

CHECK(RefTestPtr1.IsValid() == false, 		"> CReferencePtr IsValid failed.");
CHECK(s_DestructorCount == DestructorCalls,	"> CReferencePtr release failed.");

pRefTest = new TestClass();
RefTestPtr1 = pRefTest;

CHECK(pRefTest == NULL, 					"> CReferencePtr ownership on assign test failed.")

RefTestPtr1.Release();
++DestructorCalls;

CHECK(s_DestructorCount == DestructorCalls,	"> CReferencePtr object destruction failed.");

RefTestPtr1 = pNull;

CHECK(RefTestPtr1.GetReferenceCounter() == NULL, "> CReferencePtr NULL assignment failed.");

Cb::CReferencePtr<TestClass> RefTestPtr3(pNull);

CHECK(RefTestPtr3.GetReferenceCounter() == NULL, "> CReferencePtr NULL creation failed.");


//
// Smart Pointer Test
//

TestClass* pSmartTest = new TestClass();

Cb::CSmartPtr<TestClass> SmartTestPtr1(pSmartTest);

CHECK(pSmartTest == NULL, 					"> CSmartPtr ownership on constructor test failed.")

Cb::CSmartPtr<TestClass> SmartTestPtr2(SmartTestPtr1);

CHECK(SmartTestPtr1.IsValid() == false, 	"> CSmartPtr ownership on copy test failed.")

pSmartTest = SmartTestPtr2.Retain();

CHECK(pSmartTest != NULL, 					"> CSmartPtr retain test failed.")
CHECK(SmartTestPtr2.IsValid() == false, 	"> CSmartPtr ownership on retain test failed.")

SmartTestPtr1 = pSmartTest;

CHECK(pSmartTest == NULL, 					"> CSmartPtr ownership on assign test failed.")

SmartTestPtr1.Release();
++DestructorCalls;

CHECK(s_DestructorCount == DestructorCalls,	"> CSmartPtr release failed.");

pSmartTest = new TestClass();
SmartTestPtr1 = pSmartTest;
SmartTestPtr1 = pNull;
++DestructorCalls;

CHECK(s_DestructorCount == DestructorCalls,	"> CSmartPtr release on NULL assignment failed.");

pSmartTest = new TestClass();
Cb::CSmartPtr<TestClass>* pSmartTestPtr = new Cb::CSmartPtr<TestClass>(pSmartTest);

delete pSmartTestPtr;
++DestructorCalls;

CHECK(s_DestructorCount == DestructorCalls,	"> CSmartPtr object destruction failed.");

//
// Resource Pointer Test
//

TestClass* pResTest = new TestClass();

Cb::CResourcePtr<TestClass> ResTestPtr1(pResTest);

CHECK(pResTest != NULL, 					"> CResourcePtr ownership on consructor test failed.")

Cb::CResourcePtr<TestClass> ResTestPtr2(ResTestPtr1);

CHECK(pResTest->GetReferenceCount() == 2, 	"> CResourcePtr reference increment on construct failed.");

Cb::CResourcePtr<TestClass>* pResTestPtr = new Cb::CResourcePtr<TestClass>(ResTestPtr1);

CHECK(pResTest->GetReferenceCount() == 3, 	"> CResourcePtr reference increment on copy failed.");

ResTestPtr1->m_Value = 1;

CHECK(ResTestPtr2->m_Value == 1, 			"> CResourcePtr copy constructor failed.");

ResTestPtr2 = NULL;

CHECK(ResTestPtr1.GetReferenceCount() == 2, "> CResourcePtr reference decrement after NULL asignment failed.");
CHECK(ResTestPtr2.GetReferenceCount() == 0, "> CResourcePtr reference clear after NULL asignment failed.");

delete pResTestPtr;

CHECK(ResTestPtr1.GetReferenceCount() == 1, "> CResourcePtr reference decrement after destruction failed.");
CHECK(ResTestPtr1.IsLastReference() == 1, 	"> CResourcePtr IsLastReference failed.");

ResTestPtr1.Release();
++DestructorCalls;

CHECK(ResTestPtr1.IsValid() == false, 		"> CResourcePtr IsValid failed.");
CHECK(s_DestructorCount == DestructorCalls,	"> CResourcePtr release failed.");

pResTest = new TestClass();
ResTestPtr1 = pResTest;

CHECK(pResTest != NULL, 					"> CResourcePtr ownership on assign test failed.")

ResTestPtr1.Release();
++DestructorCalls;

CHECK(s_DestructorCount == DestructorCalls,	"> CResourcePtr object destruction failed.");

pResTest = new TestClass();
ResTestPtr1 = pResTest;

pResTest->KeepReference();

ResTestPtr1.Release();

CHECK(s_DestructorCount == DestructorCalls,	"> CResourcePtr keep reference failed.");
CHECK(pResTest->ReleaseReference(),			"> CResourcePtr release reference failed.");

delete pResTest;
++DestructorCalls;

END_TEST_CASE_IMPLEMENTATION